//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using System.Reflection;
using System.Text;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
#if DEBUG
    [ContractClass(typeof (BiffRecordContracts))]
    public abstract class BiffRecord
#else
    internal abstract class BiffRecord
#endif
    {
#if DEBUG
        public static bool ShowOffset = false;
       //\\ public long __offset;
        public  override string ToString()
        {
            return base.ToString();
            return ObjectFormatter.ToString(this,
                                            new List<string>()
                                                {"__offset", "_readBytes"});
        }
#endif

        protected int _length;

        private BiffRecord _next;
#if DEBUG
        //internal byte[] _readBytes;
#endif

        internal int Length
        {
            get { return _length; }
        }


        public BiffRecord Next
        {
            get { return _next; }
            set
            {
                Contract.Assert(value != this);
                _next = value;
            }
        }
        internal IEnumerable<ContinueRecord> GetFollowingContinueRecords()
        {

            ContinueRecord n = Next as ContinueRecord;
            while (n != null)
            {
                yield return n;
                n = n.Next as ContinueRecord;
            }
        }
        internal IEnumerable<T> GetFollowingRecords<T>()where T: BiffRecord
        {

            T n = Next as T;
            while (n != null)
            {
                yield return n;
                n = n.Next as T;
            }
        }

        public virtual void ReadWithContinue()
        {
        }


        internal void Read(BiffReaderContext context)
        {
#if DEBUG
           //\\this.__offset = context.BaseStream.Position - 2;
#endif
            _length = context.ReadUInt16();
#if DEBUG
            long pos = context.BaseStream.Position;
#endif
            ReadContent(context, _length);
            context.PreviousRecord = this;
#if DEBUG
            long pos2 = context.BaseStream.Position;
            Contract.Assert(pos2 - pos == _length);
            if (this is SupBookRecord)
                return;
            if (this is ExternSheetRecord)
                return;
#endif
#if CHECKWRITTENBITES
            byte[] writtenBytes = GetWrittenBytes();
          //\\  Debug.WriteLine("Checking " + this);
            if (this is SstRecord)
                return;
            if (this is DbCellRecord)
                return;
            if (! (this is SstRecord))
                Contract.Assert(readBytes.Length == writtenBytes.Length);
            for (int i = 0; i < readBytes.Length; i++)
            {
                if (!(this is XFRecord) && !(this is BiffStyleRecord) && !(this is FontRecord)&& !(this is RowRecord) )
                Contract.Assert(readBytes[i] == writtenBytes[i]);
            }
#endif
        }

#if DEBUG
        byte [] GetReadBytes (BinaryReader reader)
        {
            long pos = reader.BaseStream.Position;
            byte[] result = new byte[_length];
            reader.Read(result, 0, result.Length);
            reader.BaseStream.Position = pos;
            return result;
        }
        byte [] GetWrittenBytes ()
        {
            MemoryStream memoryStream = new MemoryStream();
            BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
            this.Write(new BiffWriterWorkbookContext(null, binaryWriter));
            binaryWriter.Flush();
            memoryStream.Position = 0;
            BinaryReader reader = new BinaryReader(memoryStream);
            BiffRecordType type = (BiffRecordType) reader.ReadUInt16();
            Contract.Assert(type == this.RecordType);
            int length = reader.ReadUInt16();
            byte[] result = new byte[length];
            reader.Read(result, 0, result.Length);
            return result;
        }
#endif

        /*protected virtual void ReadContent(BinaryReader reader, int length)
        {
            Contract.Ensures(reader.BaseStream.Position == Contract.OldValue(reader.BaseStream.Position) + length);
            reader.ReadBytes(Length);
        }*/
        protected abstract void ReadContent(BiffReaderContext context, int length);

        internal void Write(BiffWriterWorkbookContext context)
        {
            Contract.Ensures(context.Position - Contract.OldValue(context.Position) < 9000);
            var writer = context.Writer;
            writer.Write((ushort)this.RecordType);
            long startPosition = writer.BaseStream.Position;
            writer.Write((ushort)ushort.MaxValue); //\\
            //\\CleanupBeforeWrite();

            WriteContent(context);
            long endPosition = writer.BaseStream.Position;
            ushort length = (ushort) (endPosition - startPosition - 2);
            writer.BaseStream.Position = startPosition;
            writer.Write(length);
            writer.BaseStream.Position = endPosition;
        }

        internal abstract BiffRecordType RecordType{ get;}

#if DEBUG
        public string FormattedOutput
        {
            get { return ObjectFormatter.ToString(this, new List<string>(){"__offset"});} 
        }
#endif
        internal abstract void WriteContent(BiffWriterWorkbookContext context);
    }
#if DEBUG
    [ContractClassFor(typeof (BiffRecord))]
    abstract class BiffRecordContracts : BiffRecord
    {
        protected override void ReadContent(BiffReaderContext context, int length)
        {
            Contract.Requires(context != null);
            Contract.Requires(context.PreviousRecord != null || (this.GetType() == typeof (BofRecord)));
            Contract.Ensures(context.BaseStream.Position == Contract.OldValue(context.BaseStream.Position) + length);
        }
    }
#endif
}